package com.logistics.container.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.logistics.config.service.ConfigService;
import com.logistics.container.entity.domian.Container;
import com.logistics.container.entity.model.ContainerPager;
import com.logistics.container.entity.model.ContainerRecovery;
import com.logistics.container.entity.model.ContainerRow;
import com.logistics.container.entity.model.ContainerSave;
import com.logistics.container.mapper.ContainerMapper;
import com.logistics.delivery.entity.model.DeliveryContainerSave;
import com.logistics.delivery.entity.model.DeliverySave;
import com.logistics.delivery.service.DeliveryService;
import com.logistics.inventory.entity.domain.Inventory;
import com.logistics.inventory.entity.domain.InventoryDetail;
import com.logistics.inventory.service.InventoryDetailService;
import com.logistics.inventory.service.InventoryService;
import com.logistics.map.entity.domain.MapComponents;
import com.logistics.map.service.MapComponentsService;
import com.logistics.qrcode.service.QrcodeService;
import com.logistics.utils.aspect.annotation.Insert;
import com.logistics.utils.aspect.annotation.Pager;
import com.logistics.utils.bean.ResultPager;
import com.logistics.utils.bean.UserBean;
import com.logistics.utils.constants.Constants;
import com.logistics.utils.constants.Message;
import com.logistics.utils.exception.MessageException;
import com.logistics.utils.message.Manager;
import com.logistics.utils.tool.DateUtils;
import com.logistics.utils.tool.StringUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import com.logistics.utils.tool.UserUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 托盘主数据 服务实现类
 * </p>
 *
 * @author yunjian.ban
 * @since 2024-05-21
 */
@Service
public class ContainerService extends ServiceImpl<ContainerMapper, Container> {


    @Autowired
    private ConfigService configService;

    @Autowired
    private MapComponentsService mapComponentsService;

    @Autowired
    @Lazy
    private DeliveryService deliveryService;

    @Autowired
    private UserUtils userUtils;

    @Autowired
    private QrcodeService qrcodeService;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private InventoryDetailService inventoryDetailService;

    /**
     * 查找托盘列表分页
     *
     * @param param
     * @return
     */
    @Pager
    public ResultPager<ContainerRow> pager(ContainerPager param) {
        IPage<Container> page = new Page<>(param.getCurrent(), param.getPageSize());
        LambdaQueryWrapper<Container> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.like(StringUtils.isNotNull(param.getCode()), Container::getCode, param.getCode())
                .eq(StringUtils.isNotNull(param.getType()), Container::getType, param.getType());

        IPage<Container> result = this.page(page, queryWrapper);
        return new ResultPager<>(result.getRecords().stream()
                .map(ContainerRow::build).collect(Collectors.toList()), (int) result.getTotal());
    }

    /**
     * 查找所有托盘
     * @return
     */
    public List<ContainerRow> listAll(){
        return this.list().stream().map(e -> ContainerRow.build(e)).collect(Collectors.toList());
    }

    /**
     * 查找详细
     *
     * @param id
     * @return
     */
    public ContainerRow details(String id) throws MessageException {
        if (StringUtils.isNull(id)) {
            throw new MessageException(Manager.getMessage(Message.MSG1005.getCode()));
        }
        return ContainerRow.build(this.getOne(new LambdaQueryWrapper<Container>().eq(Container::getId, id)));
    }

    /**
     * 保存托盘
     *
     * @param save
     */
    @Insert
    @Transactional
    public void save(ContainerSave save) throws MessageException {
        if (StringUtils.isNull(save.getId())) {
            List<Container> containers = new ArrayList<>();

            try {
                String codePrefix = ""; // 提取前缀
                String codeSuffix = ""; // 提取数字部分
                String namePrefix = "";
                String nameSuffix = "";

                Pattern pattern = Pattern.compile("(.*-)(\\d+)");
                Matcher matcherCode = pattern.matcher(save.getCode());
                Matcher matcherName = pattern.matcher(save.getName());

                if (matcherCode.matches()) {
                    codePrefix = matcherCode.group(1);
                    codeSuffix = matcherCode.group(2);
                }
                if (matcherName.matches()) {
                    namePrefix = matcherName.group(1);
                    nameSuffix = matcherName.group(2);
                }

                int codeNum = Integer.parseInt(codeSuffix);
                int nameNum = Integer.parseInt(nameSuffix);

                int codeLength = codeSuffix.length(); // 获取数字部分的长度，用于动态格式化
                int nameLength = nameSuffix.length();

                //获取插入托盘code的最大值
                String maxCode = codePrefix + String.format("%0"+ nameLength +"d", nameNum + save.getQty());
                List<Container> containerList = this.list(new LambdaQueryWrapper<Container>().between(Container::getCode, save.getCode(), maxCode));
                if (!containerList.isEmpty()){
                    throw new MessageException("托盘已经存在,不可重复添加");
                }
                for (int i = 0; i < save.getQty(); i++) {
                    String containerCode = String.format("%0"+ codeLength +"d", codeNum + i);
                    String code = codePrefix + containerCode;
                    String containerName = String.format("%0"+ nameLength +"d", nameNum + i);
                    String name = namePrefix + containerName;
                    save.setCode(code);
                    save.setName(name);
                    containers.add(Container.build(save));
                }
            }catch (Exception e){
                throw new MessageException("托盘编号或名称输入格式错误");
            }
            this.saveBatch(containers);
        } else {
            this.update(Container.build(save),
                    new LambdaQueryWrapper<Container>().eq(Container::getId, save.getId()));
        }
    }

    /**
     * 删除托盘
     *
     * @param ids
     */
    public void delete(String ids) throws MessageException {
        if (StringUtils.isNull(ids)) {
            throw new MessageException(Manager.getMessage(Message.MSG1005.getCode()));
        }
        this.remove(new LambdaQueryWrapper<Container>().in(Container::getId, StringUtils.splitToList(ids, ",")));
    }

    /**
     * 查找托盘信息
     * @param code
     * @throws MessageException
     */
    public Container queryContainer(String code) throws MessageException{
        Container container = this.getOne(new LambdaQueryWrapper<Container>()
                .eq(Container::getCode,code));
        if(StringUtils.isNull(container)){
            throw new MessageException(Manager.getMessage(Message.MSG1014.getCode()));
        }
        return container;
    }

    /**
     * 检查托盘是否存在
     * @param codes
     */
    public void checkContainer(List<String> codes) throws  MessageException{
        for(String code : codes){
            queryContainer(code);
        }
    }

    @Override
    public boolean saveOrUpdateBatch(Collection<Container> entityList) {
        return super.saveOrUpdateBatch(entityList);
    }

    /**
     * 托盘回收
     * @param containerRecovery
     */
    @Transactional
    public void recovery(ContainerRecovery containerRecovery) throws MessageException {
        UserBean userBean = userUtils.getUserInfo();
        if (StringUtils.isNull(containerRecovery.getStation())) {
            throw new MessageException("站点编码不能为空");
        }
        if(containerRecovery.getContainers().isEmpty()){
            throw new MessageException("托盘不能为空");
        }
        // 查找所有的托盘
        List<Container> containers = containerByCode(containerRecovery.getContainers());
        List<String> containerTypes = containers.stream().map(Container::getType)
                .distinct().collect(Collectors.toList());
        List<String> containerCodes = containers.stream().map(Container::getCode).collect(Collectors.toList());
        if(containers.isEmpty() || containers.size() != containerRecovery.getContainers().size()){
            List<String> notExitsContainers = containerRecovery.getContainers().stream().
                    filter(item -> !containerCodes.contains(item)).collect(Collectors.toList());
            throw new MessageException(StringUtils.listToString(notExitsContainers, ",") + "托盘不存在");
        }
        if(containerTypes.size() > 1){
            throw new MessageException("存在不一致的托盘类型，请确认");
        }
        // 获取托盘类型
        String type = containerTypes.get(0);
        // 转码
        String point = qrcodeService.valueByCode(containerRecovery.getStation(), Constants.CODE_TYPE_LOCATION);
        // 获取系统参数获取托盘最大层
        String maxStratum = configService.valueByType(Constants.CONTAINER_MAX_STRATUM);
        if(StringUtils.isNull(maxStratum)){
            throw new MessageException("请联系管理员配置托盘存放点的层数");
        }
        // 拣配的托盘数量
        int currentStratum = containerRecovery.getContainers().size();
        // 查找可配送的站点
        List<MapComponents> locations = mapComponentsService.list(new LambdaQueryWrapper<MapComponents>().
                eq(MapComponents::getLocationArea, Constants.LOCATION_AREA_CK_KT_A).
                        eq(MapComponents::getLocationLock, false)
                        .eq(MapComponents::getContainerType, type)).
                stream().filter(e -> ((StringUtils.isNotNull(e.getCurrentStratum()) ? e.getCurrentStratum() : 0) + currentStratum) <= Integer.parseInt(maxStratum)).collect(Collectors.toList());
        if(locations.isEmpty()){
            throw new MessageException("托盘存放点容量已满，请联系相关人员进行清理");
        }
        // 卸货点
        MapComponents mapComponents = locations.get(0);
        // 查找出所有的托盘获取高度
        BigDecimal sumContainerHeight = this.list(new LambdaQueryWrapper<Container>().
                in(Container::getCode, containerRecovery.getContainers())).stream().map(e -> e.getHeight()).
                reduce(BigDecimal.valueOf(0), (a, b) -> a.add(b)).add(StringUtils.isNotNull(mapComponents.getLocationHeight()) ?
                        mapComponents.getLocationHeight() : BigDecimal.ZERO);
        // 卸货前叉起高度
        BigDecimal unloadHeight = mapComponents.getLocationHeight().add(new BigDecimal(100));

        DeliverySave deliverySave = DeliverySave.build(Constants.DELIVERY_TYPE_RECOVERY,
                point, mapComponents.getLocation(),
                Constants.AGV_TYPE_FORKLIFT, unloadHeight, mapComponents.getLocationHeight(), mapComponents.getZone());
        deliverySave.setCreateDepCode(userBean.getDep());
        deliverySave.setCreateOrgCode(userBean.getOrganization());
        deliverySave.setCreator(userBean.getUserCode());
        deliverySave.setCreateTime(DateUtils.dateToStringFormat(new Date(), DateUtils.YYYY_MM_DD_HH_MM_SS));
        deliverySave.setUpdater(userBean.getUserCode());
        deliverySave.setUpdateTime(DateUtils.dateToStringFormat(new Date(), DateUtils.YYYY_MM_DD_HH_MM_SS));
        deliverySave.setDeliveryContainers(IntStream.range(0, containerRecovery.getContainers().size()).
                mapToObj(index -> DeliveryContainerSave.
                        build(containerRecovery.getContainers().get(index),
                                index+1, Constants.CONTAINER_IS_EMPTY_YES)).collect(Collectors.toList()));
        deliveryService.save(deliverySave);
        Integer current = (StringUtils.isNotNull(mapComponents.getCurrentStratum()) ? mapComponents.getCurrentStratum() : 0) + currentStratum;
        mapComponentsService.update(MapComponents.build(sumContainerHeight, current, true), new LambdaQueryWrapper<MapComponents>().
                eq(MapComponents::getId, mapComponents.getId()));
        // 释放站点在配送完成自动释放 先注释掉
//        mapComponentsService.update(MapComponents.build(false), new LambdaQueryWrapper<MapComponents>().
//                eq(MapComponents::getLocation, point));
        List<Inventory> inventories = inventoryService.list(new LambdaQueryWrapper<Inventory>().in(Inventory::getContainer, containerRecovery.getContainers()));
        int index = mapComponents.getCurrentStratum();
        for (Inventory inventory : inventories) {
            ++index;
            inventory.setContainerLevel(index);
            inventory.setStation(mapComponents.getLocation());
            inventory.setIsEmpty(Constants.CONTAINER_IS_EMPTY_YES);
            inventory.setUpdater(userBean.getUserCode());
            inventory.setUpdateTime(new Date());
        }
        inventoryService.updateBatchById(inventories);
        inventoryDetailService.remove(new LambdaQueryWrapper<InventoryDetail>().in(InventoryDetail::getContainer, containerRecovery.getContainers()));
    }

    /**
     * 空托呼叫
     * @param code
     * @param number
     * @throws MessageException
     */
    @Transactional
    public void call(String code, Integer number) throws MessageException {
        UserBean userBean = userUtils.getUserInfo();
        // 锁定目的地站点
        List<Integer> lockLocationIds = new ArrayList<>();
        //站点码转码
        String station = qrcodeService.valueByCode(code, Constants.CODE_TYPE_LOCATION);
        if(StringUtils.isNull(station)){
            throw new MessageException("站点码不正确");
        }
        MapComponents deliverySite = mapComponentsService.getOne(new LambdaQueryWrapper<MapComponents>()
                .eq(MapComponents::getClazz, Constants.COMPONENTS_TYPE_LOCATION).
                eq(MapComponents::getLocation, station));
        if(StringUtils.isNull(deliverySite)){
            throw new MessageException("站点不存在");
        }
        // 配送站点
        lockLocationIds.add(deliverySite.getId());
        if(number <= 0){
            throw new MessageException("托盘数量不能小余等于0");
        }
        // 检查站点是否使用
        deliveryService.checkStation(station);
        // 查找可取的托盘站点
        List<MapComponents> sites = mapComponentsService.list(new LambdaQueryWrapper<MapComponents>()
                .eq(MapComponents::getLocationArea, Constants.LOCATION_AREA_CK_KT_A).
                eq(MapComponents::getLocationLock, false).
                ge(MapComponents::getCurrentStratum, number));
        if(sites.isEmpty()){
            throw new MessageException("托盘存放区没有托盘");
        }
        // 取托盘站点
        MapComponents site = sites.get(0);
        // 取盘站点
        lockLocationIds.add(site.getId());
        // 取盘站点zone
        String zone = site.getZone();
        // 托盘高度
        BigDecimal containerHeight = new BigDecimal(configService.valueByType(Constants.CONFIG_CONTAINER_HEIGHT));
        // 叉车装货时候的高度
        BigDecimal loadHeight = site.getLocationHeight().subtract(containerHeight.multiply(new BigDecimal(number)));
        // 插货高度
        BigDecimal containHeight = loadHeight.add(new BigDecimal(100));
        // 更新站点层数
        BigDecimal currentStratum = new BigDecimal(site.getCurrentStratum()).subtract(new BigDecimal(number));
        mapComponentsService.update(MapComponents.build(currentStratum.intValue(), loadHeight),
                new LambdaQueryWrapper<MapComponents>().eq(MapComponents::getId, site.getId()));
        if(StringUtils.isEquals(deliverySite.getZone(), zone)){
            // 不需要接力
            deliveryService.save(DeliverySave.build(Constants.DELIVERY_TYPE_CALL_CAR, null, deliverySite.getZone(),
                    site.getLocation(), station,
                    loadHeight,
                    containHeight,
                    Constants.AGV_TYPE_FORKLIFT, userBean));
        }else{ // 需要接力
            // 已经被占用的站点
            List<String> lockStations = inventoryService.list(new LambdaQueryWrapper<Inventory>()
                    .isNotNull(Inventory::getStation).ne(Inventory::getStation, ""))
                    .stream().map(Inventory::getStation).collect(Collectors.toList());
            // 查找接力区
            LambdaQueryWrapper<MapComponents> queryWrapper = new LambdaQueryWrapper<MapComponents>().
                    eq(MapComponents::getClazz, Constants.COMPONENTS_TYPE_LOCATION).
                    eq(MapComponents::getLocationLock, false).
                    eq(MapComponents::getLocationWcCode, deliverySite.getRelayWcCode())
                    .notIn(MapComponents::getLocation, lockStations);
            List<MapComponents> availableRelayLocations = mapComponentsService.list(queryWrapper);
            if(availableRelayLocations.isEmpty()){
                throw new MessageException("接力区站点数量不足，请确认");
            }
            // 接力区站点
            lockLocationIds.add(availableRelayLocations.get(0).getId());
            String agvType = StringUtils.isEquals(zone, Constants.PICK_AREA_LHGF)  ?
                    Constants.AGV_TYPE_FORKLIFT : Constants.AGV_TYPE_BALANCE;
            String deliveryId = deliveryService.save(DeliverySave.build(Constants.DELIVERY_TYPE_CALL_CAR,
                    null, deliverySite.getZone(),
                    site.getLocation(), availableRelayLocations.get(0).getLocation(),
                    loadHeight,
                    containHeight,
                    agvType, userBean));
            DeliverySave deliverySave = DeliverySave.build(Constants.DELIVERY_TYPE_CALL_CAR, deliveryId, zone,
                    availableRelayLocations.get(0).getLocation(),
                    station,
                    loadHeight,
                    containHeight,
                    Constants.AGV_TYPE_FORKLIFT, userBean);
            deliverySave.setStatus(Constants.DELIVERY_STATE_LOCK);
            deliveryService.save(deliverySave);
        }
        // 锁定站点
        mapComponentsService.update(MapComponents.build(true), new LambdaQueryWrapper<MapComponents>().
                in(MapComponents::getId, lockLocationIds));
    }

    /**
     * 根据code查询托盘
     * @param inventoryList
     */
    public List<Container> containerByCode(List<String> inventoryList) {
       return this.list(new LambdaQueryWrapper<Container>().in(Container::getCode, inventoryList));
    }
}
